package com.fingard.dsp.bank.directbank.citic02;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import com.fingard.constant.Format;
import com.fingard.dsp.DSPSet;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.citic02.util.OLPcrypt;
import com.fingard.dsp.bank.directbank.citic02.util.OLPdecrypt;
import com.fingard.net.ITcpClient;
import com.fingard.net.TcpPortClient;
import com.fingard.net.TcpReceiver;
import com.fingard.net.WebRequest;
import com.fingard.text.UrlHelper;
import com.fingard.util.TransIDHelper;
import com.fingard.xml.XmlTextWriter;
import com.lsy.baselib.crypto.util.Base64;


public class CITIC02Base extends DirectBase implements ITcpClient{
	private static TransIDHelper idHelper = new TransIDHelper(1, 999);
	private String tmpBody="<body>";

	protected String getQryTransID() {
		Date tmpDateNow = new Date();
		return Format.DateTime12Year2Format.format(tmpDateNow) + idHelper.getNextIDStr();
	}
	
	protected String getCharset() {
        return getBankConfig().getCharset("UTF-8");
    }
	
	protected String[] sendToBank(String tmpReqStr) throws UnsupportedEncodingException{
        WriteBankLogLn2("请求提交服务地址1：" + bankFront.serverURL);
        byte[] tmpUpBytes = tmpReqStr.getBytes(getCharset());
        String[] ipPort = UrlHelper.pickupIpAndPort(bankFront.serverURL);
		WriteBankLogStep2("POST / HTTP/1.1\r\nHost: "+ipPort[0]+":"+ipPort[1]+"\r\nContent-Type: text/xml\r\n"+"Content-Length: "+String.valueOf(tmpUpBytes.length)+"\r\n\r\n"+tmpReqStr);
		WebRequest tmpWebRequest = new WebRequest(bankFront.serverURL);
		tmpWebRequest.setConnectTimeout(10000);
		tmpWebRequest.setReadTimeout(getReadTimeout(60));//60秒
		tmpWebRequest.setSbLog(this.sbLog);
		//tmpWebRequest.setRequestProperty("connection", "Close");
		tmpWebRequest.setRequestProperty("Content-Type", "text/xml");
		String[] retStr = tmpWebRequest.upload(tmpUpBytes, getCharset());
		WriteBankLogStep3(retStr[1]);
		return retStr;
	}

	/**
	 *  外联请求
	 * @param xmlBodyDatePlain
	 * @param header
	 * @return
	 * @throws Exception
     */
	protected String[] sendToBank3(String xmlBodyDatePlain,CITIC02Header header) throws UnsupportedEncodingException {

		XmlTextWriter xmlWriter =  new XmlTextWriter();
		wirteCommenHeader(xmlWriter,header);
		String[] retStr = new String[]{"",""};
		WriteBankLogLn2("请求提交服务地址：" + bankFront.serverURL);
		WriteBankLogLn("待签名的报文原文："+xmlBodyDatePlain);
		byte[] msg = xmlBodyDatePlain.getBytes(getCharset());
		/**
		 * 参数1:要加密的报文 参数2：报文发送方私钥(key)的密码 参数3:发送方私钥文件名 参数4:发送方公钥文件名
		 * 参数5:接收方的公钥文件名
		 */
		String cusCer = getBankConfig().getValueAsString("CusCer");
		String cusKey = getBankConfig().getValueAsString("CusKey");
		String cusKeyPwd = getBankConfig().getValueAsString("CusKeyPwd");
		String bankCer = getBankConfig().getValueAsString("BankCer");
//		对业务数据进行签名加密并转成base64字符串
		byte[] encriptByte = OLPcrypt.sign_crypt(msg, cusKeyPwd,cusKey,cusCer,bankCer);
		String encriptBase64Str = new String(Base64.encode(encriptByte));
		XmlTextWriter sendBnkXml = new XmlTextWriter();
		wirteCommenHeader(sendBnkXml,header);
		sendBnkXml.writeElementString("body",encriptBase64Str);
		sendBnkXml.writeEndDocument();
		WriteBankLogStep3(retStr[1]);
		if(retStr[0].length()!=0){
			return retStr;
		}

        int indexFrom = retStr[1].indexOf(tmpBody)+tmpBody.length();
		int indexEnd = retStr[1].indexOf("</body>",indexFrom);
		String encriptStr = retStr[1].substring(indexFrom,indexEnd);
		/**
		 * 参数1：要解密的数据 参数2:接收方私钥(key)密码 参数3:接收方私钥(key)文件名 参数4:发送方公钥cer证书
		 */
		byte[] decriptByte = OLPdecrypt.decrypt_verify(Base64.decode(encriptStr.getBytes()),cusKeyPwd,
				cusKey,bankCer);
		retStr[1] = new String(decriptByte,getCharset());
		WriteBankLogLn("银行返回的明文1:"+retStr[1]);
		return retStr;
	}

	/**
	 * 供应链
 	 * @param xmlBodyDatePlain
	 * @param header
	 * @return
	 * @throws Exception
     */
	protected String[] sendToBank2(String xmlBodyDatePlain,CITIC02Header header) throws Exception {
		XmlTextWriter xmlWriter =  new XmlTextWriter();
		wirteCommenHeader(xmlWriter,header);
		String[] retStr = new String[]{"",""};
		WriteBankLogLn2("请求提交服务地址：" + bankFront.serverURL);
		WriteBankLogLn("待加密的报文原文："+xmlBodyDatePlain);

		//对业务报文进行压缩转base64
		byte [] sendTempByte = xmlBodyDatePlain.getBytes(getCharset());
		byte [] bytes = compressToByte(sendTempByte);
		String cmpMsg =new String(Base64.encode(bytes));

//		封装请求报文
		XmlTextWriter sendBnkplain = new XmlTextWriter();
		wirteCommenHeader(sendBnkplain,header);
		sendBnkplain.writeElementString("body",cmpMsg);
		sendBnkplain.writeEndDocument();
		WriteBankLogLn("发送银行的请求明文："+sendBnkplain.toXmlString());
		/**
		 * 参数1:要加密的报文 参数2：报文发送方私钥(key)的密码 参数3:发送方私钥文件名 参数4:发送方公钥文件名
		 * 参数5:接收方的公钥文件名
		 */
		String cusCer = DSPSet.replaceFilePath(getBankConfig().getValueAsString("CusCer"));
		String cusKey = DSPSet.replaceFilePath(getBankConfig().getValueAsString("CusKey"));
		String cusKeyPwd = getBankConfig().getValueAsString("CusKeyPwd");
		String bankCer =DSPSet.replaceFilePath( getBankConfig().getValueAsString("BankCer"));
		//对业务数据进行签名加密并转成base64字符串
		byte[] encriptByte = OLPcrypt.sign_crypt2(sendBnkplain.toXmlString().getBytes(getCharset()), cusKeyPwd,cusKey,cusCer,bankCer);
		byte[] bankData = upload(encriptByte);

       /**
        * 参数1：要解密的数据 参数2:接收方私钥(key)密码 参数3:接收方私钥(key)文件名 参数4:发送方公钥cer证书
        */
		byte[] decriptByte = OLPdecrypt.decrypt_verify2(bankData,cusKeyPwd,
				cusKey,bankCer);
		retStr[1] =new String(decriptByte,getCharset());
		WriteBankLogLn("银行返回的明文:"+retStr[1]);
		int indexFrom = retStr[1].indexOf(tmpBody)+tmpBody.length();
		int indexEnd = retStr[1].indexOf("</body>",indexFrom);
		String encriptStr = retStr[1].substring(indexFrom,indexEnd);
		byte[] DecryptDate = Base64.decode(encriptStr.getBytes());
		String rtmsg = uncompressToString(DecryptDate, getCharset());
		retStr[1] =rtmsg;
		WriteBankLogLn("银行返回的明文:"+retStr[1]);
		return retStr;
	}

	protected void wirteCommenHeader(XmlTextWriter xmlTextWriter, CITIC02Header header) {
		xmlTextWriter.writeStartDocByDeclaration("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
		xmlTextWriter.writeStartElement("root");
//		报文类型	stdmsgtype	是	Char(4)	0100	通知请求类0100 响应类的0110
		xmlTextWriter.writeElementString("stdmsgtype","0100" );
//		处理代码	stdprocode	是	VARChar(6)	RLTMSG	FTPMSG：文件报文（异步处理）FTPSND：文件报文（同步处理）RLTMSG：实时报文
		xmlTextWriter.writeElementString("stdprocode",header.stdprocode );
//		交易码	trdcode	是	VARChar		交易码 填写接口文档中的交易码
		xmlTextWriter.writeElementString("trdcode",header.trdcode );
//		接收方代码	receiver	是	VARChar	SCFS	中信银行发送给多个合作方时，外联平台根据此字段判断路由到哪个合作方（行外系统）。合作方发送给中信银行时，填SCFS（供应链系统简称）
		xmlTextWriter.writeElementString("receiver", "SCFS");
//		发送方代码	sender	是	VARChar	SHVW	中信银行发送给合作方时，SCFS（供应链系统简称）
		xmlTextWriter.writeElementString("sender", bankFront.corpNo);
//		发起方系统标识	srcsysid	否	VARChar		可用于内部系统的路由。中信银行内部系统标识为4位。
		xmlTextWriter.writeElementString("srcsysid", "");
//		接收方系统标识	destsysid	否	VARChar	SCFS	可用于内部系统的路由。中信银行内部系统标识为4位。
		xmlTextWriter.writeElementString("destsysid","" );
//		交易流水号	msgsendertrc	是	VARChar(20)		由发起方生成，如果是响应报文，取通知报文的流水号
		xmlTextWriter.writeElementString("msgsendertrc",header.msgsendertrc );
//		报文发送时间戳	msgsendertime	是	Char(23)		格式：yyyy-MM-dd hh:mm:ss.SSS
		xmlTextWriter.writeElementString("msgsendertime",Format.DateTime23Format.format(new Date()) );
//		文件业务类型	filetype	否	VARChar
		xmlTextWriter.writeElementString("writeElementString", "");
//		文件名称 	filename	否	VARChar	文件名如temp.zip	双方应用约定每次发送1个文件还是将多个文件打包为1个压缩文件。
		xmlTextWriter.writeElementString("filename", "");
//		文件源目录 	srcpath	否	VARChar	如out/aa	文件相对ftp home目录或用绝对路径。双方应用约定。默认使用外联平台的约定配置。
		xmlTextWriter.writeElementString("srcpath", "");
//		文件目标目录	destpath	否	VARChar	如in/bb	文件相对ftp home目录或用绝对路径。双方应用约定。默认使用外联平台的约定配置。
		xmlTextWriter.writeElementString("destpath","" );
//		完整性验证算法	filedigestalg	否	VARChar	sm3	ftp专用，支持SM3、MD5、SHA1, 建议使用国密算法SM3
		xmlTextWriter.writeElementString("filedigestalg","" );
//		hash摘要值	filedigest	否	VARChar		ftp专用，文件摘要（16进制大写字母数字，无空格）。行内核心系统因涉及转码，计算HASH不便，所以核心系统可不填，由外联平台代为计算和填写。
		xmlTextWriter.writeElementString("filedigest", "");
//		文件加密随机密钥密文	filekey	否	VARChar		ftp专用，双方约定文件是否加密，若需加密，加密方生成一个随机数作为密钥，再用接收方公钥对该随机数进行非对称加密（如RSA算法），将密文转为Base64编码的字符串。
		xmlTextWriter.writeElementString("filekey","" );
//		备注信息	remarks	否	VARChar
		xmlTextWriter.writeElementString("remarks", "");
//		返回码	retcode	否	VARChar(7)
		xmlTextWriter.writeElementString("retcode","" );
//		返回信息	retmsg	否	VARChar		出错时返回错误描述
		xmlTextWriter.writeElementString("retmsg", "");
//		主机客户号	hostno	否	VARChar
		xmlTextWriter.writeElementString("hostno", "");

	}

	@Override
	public void getContentLength(TcpReceiver p_receiver) throws Exception {
		if (p_receiver.contentLength == -1&&p_receiver.readedLength >= 10) {
				// String tmpStrReq = new
				String tmpHead = new String(p_receiver.recBytes, 0, 10, getCharset());
				WriteBankLogStep3(tmpHead);
				p_receiver.contentLength = Integer.parseInt(tmpHead);
				p_receiver.contentStartIndex = 10;

		}
	}

	protected byte[] upload(byte[] tmpBytesBody) throws Exception {

		byte[] bankData =null;

		WriteBankLogLn("2.提交银行的地址：" + bankFront.serverIP + ":" + bankFront.serverPort);

		//发送银行前置
		TcpPortClient tmpClient = new TcpPortClient(bankFront.serverIP, Integer.parseInt(bankFront.serverPort), this);
		String tmpCharset = getCharset();

		WriteBankLogStep2(new String(tmpBytesBody));
		TcpReceiver tmpReceiver = tmpClient.sendMessage(tmpBytesBody);

		if (tmpReceiver.recBytes != null) {
			bankData= new byte[tmpReceiver.readedLength];
			System.arraycopy(tmpReceiver.recBytes, 0, bankData, 0,tmpReceiver.readedLength );
			WriteBankLogStep3(new String(tmpReceiver.recBytes, 0, tmpReceiver.readedLength, tmpCharset));
			WriteBankLogStep3(new String(Base64.encode(bankData)));
		} else {
			return tmpReceiver.getConnMsg().getBytes(getCharset());
		}
		if (!tmpReceiver.isCompleted) {
			WriteBankLogLn(tmpReceiver.getConnMsg());
		}
		return bankData;
	}

	 /*
      * 字符串压缩为字节数组
      */
	public static byte[] compressToByte(byte[] bytes) {
		if (bytes.length == 0) {
			return null;
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		GZIPOutputStream gzip;
		try {
			gzip = new GZIPOutputStream(out);
			gzip.write(bytes);
			gzip.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return out.toByteArray();
	}

	public static String uncompressToString(byte[] b, String encoding) {
		if (b == null || b.length == 0) {
			return null;
		}
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		ByteArrayInputStream in = new ByteArrayInputStream(b);

		try {
			GZIPInputStream gunzip = new GZIPInputStream(in);
			byte[] buffer = new byte[256];
			int n;
			while ((n = gunzip.read(buffer)) >= 0) {
				out.write(buffer, 0, n);
			}
			return out.toString(encoding);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}
